#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "Font.h"

#include "Class.h"
#include "Error.h"
#include "String.h"
#include "WindowSystem.h"
#include "ObjArray.h"
#include "OrdColl.h"
#include "Env.h"
#include "Math.h"

Font
	*gSysFont,
	*gApplFont,
	*gFixedFont;
		
FontManager *gFontManager;
bool freal;

static Symbol FontNames[12];

//---- Font --------------------------------------------------------------------

NewMetaImpl(Font,Object, (TA(cw), TE(face), T(size), T(ils), T(ht), T(bs),
						T(width), TP(ff)));

Font *new_Font(GrFont font, int size, GrFace face)
{
	return gFontManager->MapFont(font, size, face);
}

Font *new_Font(GrFont font, int size, GrFace face, bool)
{
	freal= TRUE;
	Font *fp= gFontManager->MapFont(font, size, face);
	freal= FALSE;
	return fp;
}

Font::Font(FontFamily *f, short ps, GrFace fc)
{
	ff= f;
	size= ps;
	face= fc;
	if (ff)
		ff->fmap->Add(this);
}

Font *Font::WithFace(GrFace fc) const
{
	return gFontManager->MapFont(Fid(), size, fc);
}

Font *Font::WithSize(int sz) const
{
	return gFontManager->MapFont(Fid(), sz, face);
}

int Font::Width(register const byte *s, register int l) const
{
	register int w= 0;

	if (s) {
		if (l < 0) {
			while (*s)
				w+= cw[*s++];
		} else {
			while (--l >= 0)
				w+= cw[*s++];
		}
	}
	return w;
}

int Font::MaxWidth(int l)
{
	if (width < 0) {
		register int i;
		
		width= 0;
		for (i= 0; i< 255; i++)
			width= Math::Max((u_short)width, cw[i]);
	}
	return l*width;
}

Rectangle Font::BBox(register const byte *s, register int l) const
{
	register int w= 0;

	if (s == 0)
		return gRect0;
	if (l < 0) {
		while (*s)
			w+= cw[*s++];
	} else {
		while (--l >= 0)
			w+= cw[*s++];
	}
	return Rectangle(0, ht, w, ht+bs);
}

Metric Font::GetMetric(register const byte *s, register int l) const
{
	register int w= 0;

	if (s == 0)
		return Metric(0);
	if (l < 0) {
		while (*s)
			w+= cw[*s++];
	} else {
		while (--l >= 0)
			w+= cw[*s++];
	}
	return Metric(w, Spacing(), Ascender());
	//return Metric(w, Ascender()+Descender(), Ascender());
}

Metric Font::GetMetric(u_int c) const
{
	//return Metric(Width(c), Ascender()+Descender(), Ascender());
	return Metric(Width(c), Spacing(), Ascender());
}

Rectangle Font::BBox(register u_int c, Point pos) const
{
	return Rectangle(pos.x, pos.y-Ascender(), Width(c), Spacing());
}

Point Font::AdjustString(register const byte *s, Point p,
					GrVAdjust va, GrHAdjust ha) const
{
	register int w= 0;

	switch (ha) {
	case eAdjHRight:
		if (s)
			while (*s)
				w+= cw[*s++];
		p.x-= w;
		break;
	case eAdjHCenter:
		if (s)
			while (*s)
				w+= cw[*s++];
		p.x-= w/2;
		break;
	default:
		break;
	}

	switch (va) {
	case eAdjVBottom:
		p.y-= bs;
		break;
	case eAdjVCenter:
		p.y+= (ht-bs)/2;
		break;
	case eAdjVTop:
		p.y+= ht;
		break;
	case eAdjVBase:
		break;
	}
	return p;
}

DevBitmap *Font::CharAsBitmap(u_int, Point*)
{
	return 0;
}

const char *Font::AsString()
{
	return form("%s.%d.%s", Name(), Size(), gFontManager->StyleString(Face()));
}

u_long Font::Hash()
{
	return (u_long) Fid();
}

bool Font::IsEqual(Object *op)
{
	return op->IsKindOf(Font) && ((Font*)op)->Size() == Size()
											&& ((Font*)op)->Face() == Face();
}

OStream& Font::PrintOn(OStream &s)
{
	return s << FontNames[Fid()] SP << Size() SP << (int)Face() SP;
}

Object *Font::ReadAndMap(IStream &s)
{
	int size;
	GrFace face;
	Symbol name;
	s >> name >> size >> Enum(face);
	return new_Font(gFontManager->NameToId(name.AsString()), size, face);
}

//---- FontManager -------------------------------------------------------------

NewMetaImpl(FontManager,Object, (TP(fmap)));

FontManager::FontManager()
{
	fmap= new ObjArray;

	FontNames[0]= Symbol("Times");
	FontNames[1]= Symbol("Helvetica");
	FontNames[2]= Symbol("Courier");
	FontNames[3]= Symbol("Symbol");
	FontNames[4]= Symbol("Chicago");
	FontNames[5]= Symbol("Avantgarde");
	FontNames[6]= Symbol("Bookman");
	FontNames[7]= Symbol("Schoolbook");
	FontNames[8]= Symbol("NarrowHelvetica");
	FontNames[9]= Symbol("Palatino");
	FontNames[10]= Symbol("ZapfChancery");
	FontNames[11]= Symbol("ZapfDingbats");
}

FontManager::~FontManager()
{
	if (fmap) {
		fmap->FreeAll();
		SafeDelete(fmap);
	}
}

bool FontManager::Init()
{
	int size= Env::GetValue("Font.Size", 12);
	GrFont fid= gFontManager->NameToId(Env::GetValue("Font.Sys", "Chicago"));
	gSysFont= new_Font(fid, size);
	fid= gFontManager->NameToId(Env::GetValue("Font.Appl", "Helvetica"));
	gApplFont= new_Font(fid, size);
	fid= gFontManager->NameToId(Env::GetValue("Font.Fixed", "Courier"));
	gFixedFont= new_Font(fid, size);
	return FALSE;
}

FontFamily *FontManager::MapFamily(GrFont fid)
{
	FontFamily *fm;

	if ((fm= (FontFamily*) fmap->At(fid)) == 0) {
		if (fid < eFontTimes || fid > eFontDingbats)
			return (FontFamily*) fmap->At(eFontChicago);
		fmap->AtPutAndExpand(fid, fm= new FontFamily(fid));
	}
	return fm;
}
		
Font *FontManager::MapFont(GrFont fid, int size, GrFace face)
{
	return MapFamily(fid)->MapSizeFace(size, face);
}

Font *FontManager::MakeFont(FontFamily*, int, GrFace, bool)
{
	return 0;
}

Font *FontManager::ScaleFont(FontFamily *ff, int size, GrFace face)
{
	Font *f;
	int facebit;

	if (face != eFacePlain) {
		for (facebit= eFaceShadow; facebit; facebit >>= 1) {
			if (face & facebit) {
				face= (GrFace) (face & ~facebit);
				return ff->MapSizeFace(size, face);
			}
		}
	}
	for (int i= 6; i < 24; i++) {
		if ((size-i > 6) && (f= ff->MapSizeFace2(size-i, face)))
			return f;
		if ((size+i > 24) && (f= ff->MapSizeFace2(size+i, face)))
			return f;
	}
	return 0;
}

GrFont FontManager::NameToId(const char *fontname) const
{
	Symbol name(fontname);
	for (int i= 0; i < sizeof(FontNames) / sizeof(FontNames[0]); i++)
		if (FontNames[i] == name)
			return (GrFont)i;
	return (GrFont)-1;
}

const char *FontManager::IdToName(GrFont fid) const
{
	if (fid >= 0 && fid < sizeof(FontNames) / sizeof(FontNames[0]))
		return FontNames[fid].AsString();
	return 0;
}

const char *FontManager::StyleString(GrFace face) const
{
	static char buf[20];
	char *cp= buf;
	if (face == eFacePlain)
		*cp++= 'r';
	else {
		if (face & eFaceBold)
			*cp++= 'b';
		if (face & eFaceItalic)
			*cp++= 'i';
		if (face & eFaceUnderline)
			*cp++= 'u';
		if (face & eFaceOutline)
			*cp++= 'o';
		if (face & eFaceShadow)
			*cp++= 's';
	}
	*cp= '\0';
	return buf;
}

void FontManager::CollectParts(class Collection *col)
{
	col->Add(fmap);
}

//---- FontFamily --------------------------------------------------------------

NewMetaImpl(FontFamily,Object, (TE(fid), TB(fixed), TP(fmap), TP(encoding)));

FontFamily::FontFamily(GrFont f)
{
	fid= f;
	fmap= new OrdCollection;
	encoding= (FontEncoding*)gStdEncoding;
}

FontFamily::~FontFamily()
{
	if (fmap) {
		fmap->FreeAll();
		SafeDelete(fmap);
	}
}

const char *FontFamily::Name() const
{
	return FontNames[fid].AsString();
}

Font *FontFamily::MapSizeFace2(int size, GrFace face, bool dflt)
{
	Iter next(fmap);
	register Font *fp;

	// if (! freal) {
	while (fp= (Font*) next())
		if (fp->Size() == size && fp->Face() == face)
			return fp;
	// }
	freal= FALSE;
	return gFontManager->MakeFont(this, size, face, dflt);
}

Font *FontFamily::MapSizeFace(int size, GrFace face)
{
	Font *f;

	if (f= MapSizeFace2(size, face))
		return f;
	if (f= gFontManager->ScaleFont(this, size, face))
		return f;
	return MapSizeFace2(0, face, TRUE);
}

void FontFamily::InspectorId(char *buf, int bufSize)
{
	strncpy(buf, Name(), bufSize);
}

void FontFamily::CollectParts(Collection *col)
{
	col->Add(fmap);
}

